场景介绍
我们考虑简单的客户端——服务器通信的场景,其典型模式为:
服务端通过close()主动关闭一个TCP连接
客户端通过read()获得了0(表示服务端没有数据),调用close()关闭这个连接。
在TCP层面表现为:
服务器调用close()后,向客户端发送FIN,客户端回应FIN-ACK。服务器进入FIN-WAIT-2状态,客户端进入CLOSE-WAIT状态。
客户端调用close()后,向服务端发送FIN,服务端会用FIN-ACK。服务端进入TIME-WAIT状态,客户端直接进入CLOSE状态,连接结束。
我们考虑一些异常情况,客户端上:
客户端在获得read()==0后,没有及时地调用close()调用;
客户端在获得read()==0后,仍然向服务端写入数据。
测试程序
选用《UNIX网络编程》(第一卷)中的服务器——客户端样例程序。服务器部分:
int main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t len;
struct sockaddr_in servaddr, cliaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999); /* daytime server */
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for ( ; ; ) {
len = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &len);
printf("connection from %s, port %d\n",
Inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff));
Close(connfd);
}
}
服务器监听9999端口,新链接建立后,发出当前时间,随后调用close()关闭连接。
客户端部分:
int main(int argc, char **argv)
{
int sockfd, n;
socklen_t len;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr, cliaddr;
if (argc != 2)
err_quit("usage: a.out <IPaddress>");
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9999); /* daytime server */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
len = sizeof(cliaddr);
Getsockname(sockfd, (SA *) &cliaddr, &len);
printf("local addr: %s\n",
Sock_ntop((SA *) &cliaddr, sizeof(cliaddr)));
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF)
err_sys("fputs error");
}
if (n < 0)
err_sys("read error");
exit(0);
}
客户端没有及时地调用close()调用
我们修改客户端调用close()的处理流程。当read()==0时,阻塞睡眠。等待一段时间后,发送SIGINT使进程退出,相当于调用close()。注意到以下的现象:
服务端SOCKET处于FIN-WAIT-2状态时,发送SIGINT信号使客户端退出,客户端发送FIN,服务端回复FIN-ACK。此时,按正常流程结束链接。
服务端SOCKET等待FIN-WAIT-2状态超时后,客户端发送FIN,服务端回复RST结束链接。
Orphan Socket: 从应用程序来看,此条socket连接已经收发数据完毕,关闭了此连接,但是linux内核中为了完成正常的tcp协议(比如缓冲区中的数据)转换,会在内核的tcp协议层继续维护这些sock状态,直至系统回收。处于此种状态下的socket就是orphan socket。
分析:
服务端SOCKET关闭后,没有对这一SOCKET的引用。这一SOCKET进入到“孤儿SOCKET“的状态。孤儿Socket存在时,系统协议栈负责完成后续的FIN流程。当孤儿Socket超时后,系统协议栈将不存在这一Socket的信息。客户端此时发送FIN,将收到RST应答。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。